package com.example.docmanagement.service;

import com.example.docmanagement.dto.request.CategoryCreateRequest;
import com.example.docmanagement.dto.response.CategoryResponse;
import com.example.docmanagement.entity.Category;
import com.example.docmanagement.exception.DuplicateResourceException;
import com.example.docmanagement.exception.ResourceNotFoundException;
import com.example.docmanagement.repository.CategoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 分类服务层
 * 处理分类相关的业务逻辑
 * 
 * 缓存策略：
 * - 分类树：缓存 1 小时
 * - 分类详情：缓存 1 小时
 * - 更新/删除时清除相关缓存
 */
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@CacheConfig(cacheNames = "categories") // 默认缓存名称
public class CategoryService {

    private final CategoryRepository categoryRepository;

    /**
     * 获取所有分类（树形结构）
     * 优化：一次性加载所有分类及其子分类，避免 N+1 查询
     * 缓存：1 小时
     */
    @Cacheable(key = "'tree'")
    public List<CategoryResponse> getAllCategories() {
        // 使用 JOIN FETCH 一次性加载所有分类及其子分类
        List<Category> allCategories = categoryRepository.findAllWithRelations();

        // 筛选根分类（无父分类）
        List<Category> rootCategories = allCategories.stream()
                .filter(c -> c.getParent() == null)
                .collect(Collectors.toList());

        return rootCategories.stream()
                .map(this::convertToTreeResponse)
                .collect(Collectors.toList());
    }

    /**
     * 获取所有分类（扁平列表）
     * 优化：使用构造函数表达式直接返回 DTO，避免 N+1 查询
     */
    public List<CategoryResponse> getAllCategoriesFlat() {
        return categoryRepository.findAllFlatWithParent();
    }

    /**
     * 根据 ID 获取分类详情
     * 缓存：1 小时
     */
    @Cacheable(cacheNames = "categoryDetail", key = "'id:' + #id", unless = "#result == null")
    public CategoryResponse getCategoryById(Long id) {
        Category category = categoryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("分类", "id", id));
        return convertToResponse(category);
    }

    /**
     * 根据 slug 获取分类详情
     * 缓存：1 小时
     */
    @Cacheable(cacheNames = "categoryDetail", key = "'slug:' + #slug", unless = "#result == null")
    public CategoryResponse getCategoryBySlug(String slug) {
        Category category = categoryRepository.findBySlug(slug)
                .orElseThrow(() -> new ResourceNotFoundException("分类", "slug", slug));
        return convertToResponse(category);
    }

    /**
     * 创建分类
     * 清除分类树缓存
     */
    @CacheEvict(key = "'tree'")
    @Transactional
    public CategoryResponse createCategory(CategoryCreateRequest request) {
        // 检查 slug 是否重复
        if (categoryRepository.existsBySlug(request.getSlug())) {
            throw new DuplicateResourceException("slug", request.getSlug());
        }

        // 构建分类实体
        Category category = Category.builder()
                .name(request.getName())
                .slug(request.getSlug())
                .description(request.getDescription())
                .icon(request.getIcon())
                .sortOrder(request.getSortOrder())
                .docCount(0)
                .isActive(true)
                .build();

        // 设置父分类
        if (request.getParentId() != null) {
            Category parent = categoryRepository.findById(request.getParentId())
                    .orElseThrow(() -> new ResourceNotFoundException("父分类", "id", request.getParentId()));
            category.setParent(parent);
        }

        // 保存分类
        Category saved = categoryRepository.save(category);
        return convertToResponse(saved);
    }

    /**
     * 更新分类
     * 清除分类树缓存和分类详情缓存
     */
    @Caching(evict = {
            @CacheEvict(key = "'tree'"),
            @CacheEvict(cacheNames = "categoryDetail", key = "'id:' + #id")
    })
    @Transactional
    public CategoryResponse updateCategory(Long id, CategoryCreateRequest request) {
        Category category = categoryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("分类", "id", id));

        // 检查 slug 是否重复（排除自己）
        if (request.getSlug() != null && !request.getSlug().equals(category.getSlug())) {
            if (categoryRepository.existsBySlug(request.getSlug())) {
                throw new DuplicateResourceException("slug", request.getSlug());
            }
            category.setSlug(request.getSlug());
        }

        // 更新字段
        if (request.getName() != null)
            category.setName(request.getName());
        if (request.getDescription() != null)
            category.setDescription(request.getDescription());
        if (request.getIcon() != null)
            category.setIcon(request.getIcon());
        if (request.getSortOrder() != null)
            category.setSortOrder(request.getSortOrder());

        // 更新父分类
        if (request.getParentId() != null) {
            Category parent = categoryRepository.findById(request.getParentId())
                    .orElseThrow(() -> new ResourceNotFoundException("父分类", "id", request.getParentId()));
            category.setParent(parent);
        }

        Category updated = categoryRepository.save(category);
        return convertToResponse(updated);
    }

    /**
     * 删除分类
     * 清除分类树缓存和分类详情缓存
     */
    @Caching(evict = {
            @CacheEvict(key = "'tree'"),
            @CacheEvict(cacheNames = "categoryDetail", key = "'id:' + #id")
    })
    @Transactional
    public void deleteCategory(Long id) {
        Category category = categoryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("分类", "id", id));

        // 检查是否有文档
        if (category.getDocCount() > 0) {
            throw new IllegalArgumentException(
                    String.format("该分类下还有 %d 个文档，无法删除", category.getDocCount()));
        }

        categoryRepository.delete(category);
    }

    /**
     * 转换为响应 DTO（不包含子分类）
     */
    private CategoryResponse convertToResponse(Category category) {
        return CategoryResponse.builder()
                .id(category.getId())
                .name(category.getName())
                .slug(category.getSlug())
                .description(category.getDescription())
                .icon(category.getIcon())
                .parentId(category.getParent() != null ? category.getParent().getId() : null)
                .sortOrder(category.getSortOrder())
                .docCount(category.getDocCount())
                .isActive(category.getIsActive())
                .createdAt(category.getCreatedAt())
                .updatedAt(category.getUpdatedAt())
                .build();
    }

    /**
     * 转换为树形响应 DTO（包含子分类）
     */
    private CategoryResponse convertToTreeResponse(Category category) {
        CategoryResponse response = convertToResponse(category);

        // 递归转换子分类
        if (!category.getChildren().isEmpty()) {
            List<CategoryResponse> children = category.getChildren().stream()
                    .map(this::convertToTreeResponse)
                    .collect(Collectors.toList());
            response.setChildren(children);
        }

        return response;
    }
}
